//
//  StaticController.hpp
//  web-starter-project
//
//  Created by Leonid on 2/12/18.
//  Copyright © 2018 oatpp. All rights reserved.
//

#ifndef StaticController_hpp
#define StaticController_hpp

#include "dto/MyDTOs.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"

#include "json/json.h"
#include "json/reader.h"

#include "CommUtils.hpp"

// #include "SitePath.hpp"
#include "FilesIndex.hpp"
#include "StaticFileManager.hpp"

#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/macro/component.hpp"

#include <unordered_map>
#include <iostream>

/**
 *  EXAMPLE ApiController
 *  Basic examples of howto create ENDPOINTs
 *  More details on oatpp.io
 */
class StaticController : public oatpp::web::server::api::ApiController {
public:
  typedef StaticController __ControllerType;
protected:
  StaticController(const std::shared_ptr<ObjectMapper>& objectMapper)
    : oatpp::web::server::api::ApiController(objectMapper)
  {}
public:
  // AresDing 该宏定义 为StaticController类定义了filesIndex和staticFileManager两个shared_ptr 并默认初始化为Environment中已经注册好的对象
  OATPP_COMPONENT(std::shared_ptr<FilesIndex>, filesIndex);
  // std::shared_ptr<FilesIndex>& filesIndex = (*((std::shared_ptr<FilesIndex>*) oatpp::base::Environment::getComponent(typeid(std::shared_ptr<FilesIndex>).name())));
  OATPP_COMPONENT(std::shared_ptr<StaticFileManager>, staticFileManager);
  // std::shared_ptr<StaticFileManager>& staticFileManager = (*((std::shared_ptr<StaticFileManager>*) oatpp::base::Environment::getComponent(typeid(std::shared_ptr<StaticFileManager>).name())));
public:
  
  /**
   *  Inject @objectMapper component here as default parameter
   *  Do not return bare Controllable* object! use shared_ptr!
   */
  static std::shared_ptr<StaticController> createShared(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>,
                                                                      objectMapper)){
    return std::shared_ptr<StaticController>(new StaticController(objectMapper));
  }
  
  /**
   *  Begin ENDPOINTs generation ('ApiController' codegen)
   */
#include OATPP_CODEGEN_BEGIN(ApiController)

  #if 0
  ENDPOINT_ASYNC("GET", "/robots.txt", Robots) {

    ENDPOINT_ASYNC_INIT(Robots)

    Action act() override {
      const char* robots =
        "User-agent: *\n"
        "Disallow:\n"
        "\n"
        "# Sitemap files\n"
        "Sitemap: https://oatpp.io/sitemap/\n";
      auto response = controller->createResponse(Status::CODE_200, robots);
      response->putHeader(Header::CONTENT_TYPE, "text/plain; charset=utf-8");
      return _return(response);
    }

  };

  ENDPOINT_ASYNC("GET", "/sitemap", GetSitemap) {

    ENDPOINT_ASYNC_INIT(GetSitemap)

    Action act() override {
      auto response = controller->createResponse(Status::CODE_200, controller->filesIndex->getSitemap().getIndexFile());
      response->putHeader(Header::CONTENT_TYPE, "text/xml");
      return _return(response);
    }

  };

  ENDPOINT_ASYNC("GET", "/sitemap/{page}", GetSitemapUrls) {

    ENDPOINT_ASYNC_INIT(GetSitemapUrls)

    v_int32 verifyInt(const oatpp::String& value) const {
      bool success;
      auto pageIndexText = value;
      OATPP_ASSERT_HTTP(pageIndexText, Status::CODE_400, "Please specify correct page number");
      auto intValue = oatpp::utils::conversion::strToInt32(pageIndexText, success);
      OATPP_ASSERT_HTTP(success, Status::CODE_400, "Please specify correct page number");
      return intValue;
    }

    Action act() override {
      auto pageIndex = verifyInt(request->getPathVariable("page"));
      auto response = controller->createResponse(Status::CODE_200, controller->filesIndex->getSitemap().getUrlsFile(pageIndex));
      response->putHeader(Header::CONTENT_TYPE, "text/xml");
      return _return(response);
    }

  };

  ENDPOINT_ASYNC("GET", "/docs/start/step_by_step", StepByStep) {
    
    ENDPOINT_ASYNC_INIT(StepByStep)
    
    Action act() override {
      auto response = OutgoingResponse::createShared(oatpp::web::protocol::http::Status::CODE_301, nullptr);
      response->putHeader("Location", oatpp::String("") + SitePath::CanonicalBase + "/docs/start/step-by-step/");
      return _return(response);
    }
    
  };
  
  ENDPOINT_ASYNC("GET", "/docs/simple_vs_async", SimpleVsAsync) {
    
    ENDPOINT_ASYNC_INIT(SimpleVsAsync)
    
    Action act() override {
      auto response = OutgoingResponse::createShared(oatpp::web::protocol::http::Status::CODE_301, nullptr);
      response->putHeader("Location", oatpp::String("") + SitePath::CanonicalBase + "/docs/simple-vs-async/");
      return _return(response);
    }
    
  };

  ENDPOINT_ASYNC("GET", "/docs/integrations/swagger", IntegrationsSwagger) {

    ENDPOINT_ASYNC_INIT(IntegrationsSwagger)

    Action act() override {
      auto response = OutgoingResponse::createShared(oatpp::web::protocol::http::Status::CODE_301, nullptr);
      response->putHeader("Location", oatpp::String("") + SitePath::CanonicalBase + "/https://oatpp.io/docs/modules/oatpp-swagger/");
      return _return(response);
    }

  };
  
  #endif

  ENDPOINT_ASYNC("POST", "/login", post_login) {

      ENDPOINT_ASYNC_INIT(post_login)

       Action act() override {
          /* return Action to start child coroutine to read body */
          return request->readBodyToStringAsync().callbackTo(&post_login::returnResponse);
      }
      
      Action returnResponse(const oatpp::String& body){
          /* return Action to return created OutgoingResponse */

          std::string newId = getUuid();      // 每次Http请求生成一次会话Id
          std::cout << "SessionId = " << newId << std::endl;

          std::string json_str = (std::string)body;
          Json::Reader jsReader;
          Json::Value jsObj;
          if (jsReader.parse(json_str, jsObj)) {
              std::string strName = jsObj["username"].asString();
              std::string strPasswd = jsObj["password"].asString();
              std::cout << "username = " << strName << std::endl;
              std::cout << "password = " << strPasswd << std::endl;
              return _return(controller->createResponse(Status::CODE_200, body));
          } else {
            // 解析json错误
            Json::Value result;
            result["msg"] = "json为空";
            result["success"] = false;
            std::string strResult = result.toStyledString();
            return _return(controller->createResponse(Status::CODE_501, strResult));
          }   
      }

      #if 0
      Action act() override {
        std::cout << "post_login act()" << std::endl;
        return request->readBodyToDtoAsync<oatpp::Object<PostLoginDTO>>(controller->getDefaultObjectMapper()).callbackTo(&post_login::returnResponse);
      }

      Action returnResponse(const oatpp::Object<PostLoginDTO>& body){
        return _return(controller->createDtoResponse(Status::CODE_200, "dtoMessage: " + body->message));
      }

      #else

      #if 0
      Action act() override {
        std::cout << "post_login act()" << std::endl;
        return request->readBodyToDtoAsync<oatpp::Object<PostLoginDTO>>(controller->getDefaultObjectMapper())
                      .callbackTo(&post_login::onBodyObtained);
      }

      Action onBodyObtained(const oatpp::Object<PostLoginDTO>& dto) {

        // oatpp json https://oatpp.io/docs/components/dto/#serialize-deserialize
        // std::cout << "login:message = " << (std::string)dto->message << std::endl;
        // std::cout << "login:username = " << (std::string)dto->username << std::endl;
        // std::cout << "login:password = " << (std::string)dto->password << std::endl; 

        auto jsonObjectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
        oatpp::String json = jsonObjectMapper->writeToString(dto);
        OATPP_LOGD("json", "value='%s'", json->c_str());

        return _return(controller->createResponse(Status::CODE_200, "Done"));
      }
      #endif

      #endif

  };

  #if 1
  ENDPOINT_ASYNC("GET", "*", Static) {
    
    ENDPOINT_ASYNC_INIT(Static)
    
    Action act() override {

      std::cout << "Static Request" << std::endl;

      auto tail = request->getPathTail();
      OATPP_ASSERT_HTTP(tail, Status::CODE_400, "Empty filename");

      oatpp::parser::Caret caret(tail);

      auto pathLabel = caret.putLabel();
      caret.findChar('?');

      auto path = pathLabel.toString();

      auto queryLabel = caret.putLabel();
      caret.setPosition(tail->size());

      oatpp::parser::Caret pathCaret(path);

      /* redirect from non canonical urls */
      if(path->size() > 0 && path->data()[path->size() - 1] != '/' && !pathCaret.findChar('.')) {
        auto response = OutgoingResponse::createShared(oatpp::web::protocol::http::Status::CODE_301, nullptr);
        // oatpp::data::stream::FIFOInputStream stream;
        // stream.writeSimple(SitePath::CanonicalBase);
        // stream.writeSimple("/", 1);
        // stream.writeSimple(path);
        // stream.writeSimple("/", 1);
        // stream.writeSimple(queryLabel.toString());
        // response->putHeader("Location", stream.toString());
        return _return(response);
      }

      auto info = controller->filesIndex->getFileInfo(path);
      if(!info) {
        info = controller->filesIndex->getFileInfo(path + "/");
      }
      OATPP_ASSERT_HTTP(info, Status::CODE_404, "Page not found");

      auto buffer = controller->staticFileManager->getFile(info->path, true);
      OATPP_ASSERT_HTTP(buffer, Status::CODE_500, "Can't read file");

      return _return(controller->createResponse(Status::CODE_200, buffer));
    }
    
  };
  #endif
  
  /**
   *  Finish ENDPOINTs generation ('ApiController' codegen)
   */
#include OATPP_CODEGEN_END(ApiController)
  
};

#endif /* StaticController_hpp */
